#import "IMIDIDevManager.h"
#import <CoreAudioKit/CABTMIDICentralViewController.h>
#import <MIKMIDI/MIKMIDI.h>

#pragma mark - private tools

static NSString* strOrUnknown(NSString* src) { return (src && src.length > 0) ? src : @"未知"; }

static NSString* strYesNo(BOOL b) { return b ? @"yes" : @"no"; }

static BOOL isValidDevice(const MIKMIDIDevice* dev) {
    // 不处理虚拟设备和离线设备
    if (dev.isVirtual || !dev.isOnline) return NO;
    // 判断是否是 USB 设备或者有效的蓝牙设备
    NSDictionary* properties = [dev propertiesDictionary];
    if (![properties objectForKey:@"USBLocationID"] &&
        ![properties objectForKey:@"MIDI Input Supported"])
        return NO;
    return YES;
}

static UIViewController* getCurrentViewController() {
    UIWindow* keyWindow = [UIApplication sharedApplication].keyWindow;
    UIViewController* vc = keyWindow.rootViewController;
    while (vc.presentedViewController) {
        vc = vc.presentedViewController;

        if ([vc isKindOfClass:[UINavigationController class]]) {
            vc = [(UINavigationController*)vc visibleViewController];
        } else if ([vc isKindOfClass:[UITabBarController class]]) {
            vc = [(UITabBarController*)vc selectedViewController];
        }
    }
    return vc;
}

#pragma mark - IMIDIDevManager private attr
@interface IMIDIDevManager ()

@property(nonatomic, strong) id sourceConnectionToken;
@property(nonatomic) MIDIUniqueID connectedSourceDevId;
@property(nonatomic, strong) NSString* connectedSourceDevName;
@property(nonatomic, strong) NSMutableArray<IMIDIDevManagerEventHandler>* handlers;  // 用于数据缓存
@property(nonatomic, strong) dispatch_queue_t queue;

@end

#pragma mark - IMIDIDevManager

@implementation IMIDIDevManager

#pragma mark - IMIDIDevManager private interface

+ (id)allocWithZone:(struct _NSZone*)zone {
    return [IMIDIDevManager defaultManager];
}

- (id)copyWithZone:(struct _NSZone*)zone {
    return [IMIDIDevManager defaultManager];
}

- (void)setup {
    _connectedSourceDevId = kMIDIInvalidUniqueID;
    _sourceConnectionToken = nil;
    _handlers = [[NSMutableArray alloc] init];
    _queue = dispatch_queue_create("com.baroxtech.IMIDIDevManager", NULL);
    _running = NO;
}

- (void)handleEvent:(IMIDIDevManagerEventType)eventType data:(nonnull id)data {
    dispatch_async(_queue, ^{
      for (IMIDIDevManagerEventHandler handler in self.handlers) {
          handler(eventType, data);
      }
    });
}

- (void)deviceWasUnplugged:(NSNotification*)notification {
    MIKMIDIDevice* dev = [notification userInfo][MIKMIDIDeviceKey];
    // 尝试断开已经链接的设备
    [self disconnectDevice:dev];
}

- (void)deviceWasPlugged:(NSNotification*)notification {
    MIKMIDIDevice* dev = [notification userInfo][MIKMIDIDeviceKey];
    // 尝试链接该设备
    [self connectDevice:dev];
}

- (BOOL)connectDevice:(MIKMIDIDevice*)dev {
    NSString* displayName = strOrUnknown(dev.displayName);
    NSLog(@"\t设备名: %@, 显示名: %@, ID: %d, 生产商: %@, 型号: %@，是否虚拟设备: %@, "
          @"是否在线: %@\n",
          strOrUnknown(dev.name), displayName, dev.uniqueID, strOrUnknown(dev.manufacturer),
          strOrUnknown(dev.model), strYesNo(dev.isVirtual), strYesNo(dev.isOnline));

    if (self.isSourceConnected) return YES;
    if (!isValidDevice(dev)) return NO;

    MIKMIDIDeviceManager* manager = MIKMIDIDeviceManager.sharedDeviceManager;
    __weak typeof(self) weakSelf = self;
    for (MIKMIDIEntity* entity in dev.entities) {
        // 尝试链接任一端口，一旦成功就返回
        for (MIKMIDISourceEndpoint* endpoint in entity.sources) {
            _sourceConnectionToken = [manager
                connectInput:endpoint
                       error:nil
                eventHandler:^(MIKMIDISourceEndpoint* _Nonnull source,
                               NSArray<MIKMIDICommand*>* _Nonnull commands) {
                  __strong typeof(weakSelf) strongSelf = weakSelf;
                  [strongSelf handleEvent:kIMIDIDevManagerEvent_MIDICommands data:commands];
                }];
            if (_sourceConnectionToken) {
                _connectedSourceDevId = dev.uniqueID;
                _connectedSourceDevName = displayName;
                [self handleEvent:kIMIDIDevManagerEvent_SourceConncted data:displayName];
                return YES;
            }
        }
    }
    return NO;
}

- (void)disconnectDevice:(MIKMIDIDevice*)dev {
    if (!self.isSourceConnected) return;
    // 判定是否为当前已经链接的设备
    if (dev && dev.uniqueID != _connectedSourceDevId) return;
    // 断开链接
    [MIKMIDIDeviceManager.sharedDeviceManager disconnectConnectionForToken:_sourceConnectionToken];
    _connectedSourceDevId = kMIDIInvalidUniqueID;
    _sourceConnectionToken = nil;
    [self handleEvent:kIMIDIDevManagerEvent_SourceDisconncted data:_connectedSourceDevName];
    _connectedSourceDevName = nil;
    // 尝试链接可用
    [self connectFirstValidDevice];
}

- (void)connectFirstValidDevice {
    if (!_running) return;

    NSArray* devices = [MIKMIDIDeviceManager.sharedDeviceManager availableDevices];
    if (!devices || devices.count == 0) return;

    NSLog(@"已经发现的 midi 设备有 %lu 个:\n", devices.count);
    for (MIKMIDIDevice* dev in devices) {
        if ([self connectDevice:dev]) return;
    }
}

- (void)doneAction:(id)sender {
    UIViewController* vc = getCurrentViewController();
    [vc.navigationController dismissViewControllerAnimated:YES completion:nil];
}

#pragma mark - IMIDIDevManager public interface

+ (instancetype)defaultManager {
    static IMIDIDevManager* _instance = nil;
    if (!_instance) {
        _instance = [[super allocWithZone:NULL] init];
        [_instance setup];
    }
    return _instance;
}

- (BOOL)isSourceConnected {
    return _sourceConnectionToken ? YES : NO;
}

- (void)setRunning:(BOOL)running {
    if (_running == running) return;
    _running = running;
    if (running) {
        // 注册设备连接与断开的通知
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(deviceWasUnplugged:)
                                                     name:MIKMIDIDeviceWasRemovedNotification
                                                   object:MIKMIDIDeviceManager.sharedDeviceManager];
        [[NSNotificationCenter defaultCenter] addObserver:self
                                                 selector:@selector(deviceWasPlugged:)
                                                     name:MIKMIDIDeviceWasAddedNotification
                                                   object:MIKMIDIDeviceManager.sharedDeviceManager];
        // 枚举设备列表，执行链接
        [self connectFirstValidDevice];
    } else {
        [[NSNotificationCenter defaultCenter] removeObserver:self];
        // 尝试断开链接
        [self disconnectDevice:nil];
    }
}

- (void)showBLEListView {
    CABTMIDICentralViewController* viewController = [CABTMIDICentralViewController new];

    UINavigationController* navController =
        [[UINavigationController alloc] initWithRootViewController:viewController];
    viewController.navigationItem.rightBarButtonItem =
        [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemDone
                                                      target:self
                                                      action:@selector(doneAction:)];

    navController.modalPresentationStyle = UIModalPresentationPageSheet;
    // 获取当前 view controller 展示选择视图
    UIViewController* currVC = getCurrentViewController();
    [currVC presentViewController:navController animated:YES completion:nil];
}

- (void)addEventHandler:(IMIDIDevManagerEventHandler)handler {
    dispatch_sync(_queue, ^{
      if ([self.handlers indexOfObject:handler] == NSNotFound) {
          [self.handlers addObject:handler];
      }
    });
}

- (void)removeEventHandler:(IMIDIDevManagerEventHandler)handler {
    dispatch_sync(_queue, ^{
      [self.handlers removeObject:handler];
    });
}

@end
